package com.echo.boot.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * Created with IntelliJ IDEA
 * Created By CQ
 * Date: 2020/3/26
 * Time: 10:07
 *
 * @author Administrator
 * <p>
 * 在软件开发中，常用的对称加密算法有：
 * 算法	密钥长度	工作模式	填充模式
 * DES	56/64	ECB/CBC/PCBC/CTR/...	NoPadding/PKCS5Padding/...
 * AES	128/192/256	ECB/CBC/PCBC/CTR/...	NoPadding/PKCS5Padding/PKCS7Padding/...
 * IDEA	128	ECB	PKCS5Padding/PKCS7Padding/...
 */
public class AESUtils {
    // 算法 ASC
    // 密匙长度 128/192/256 位
    // 工作模式 ECB/CBC/PCBC/CTR/...
    // 填充模式 NoPadding/PKCS5Padding/PKCS&Padding/...
    public static void main(String[] args) throws Exception {
        // 原文:
//        String message = "Hello, world!";
//        System.out.println("Message: " + message);
//        // 128位密钥 = 16 bytes Key:
//        byte[] key = "1234567890abcdef".getBytes("UTF-8");
//        // 加密:
//        byte[] data = message.getBytes("UTF-8");
//        byte[] encrypted = ecbEncrypt(key, data);
//        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
//        // 解密:
//        byte[] decrypted = ecbDecrypt(key, encrypted);
//        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));

//        ===================================================================
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message: " + message);
        // 256位密钥 = 32 bytes Key:
        byte[] key = "1234567890abcdef1234567890abcdef".getBytes("UTF-8");
        // 加密:
        byte[] data = message.getBytes("UTF-8");
        byte[] encrypted = cbcEncrypt(key, data);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        System.out.println("Encrypted: " + Base64.getEncoder().withoutPadding().encodeToString(encrypted));
        // 解密:
        byte[] decrypted = cbcDecrypt(key, encrypted);
        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));

    }


    // AES/ECB/PKCS5Padding encrypt
    public static byte[] ecbEncrypt(byte[] key, byte[] input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    // AES/ECB/PKCS5Padding decrypt
    public static byte[] ecbDecrypt(byte[] key, byte[] input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    // AES/CBC/PKCS5Padding decrypt
    public static byte[] cbcEncrypt(byte[] key, byte[] input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        byte[] data = cipher.doFinal(input);
        return join(iv, data);
    }

    public static byte[] cbcDecrypt(byte[] key, byte[] input) throws Exception {

        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        System.arraycopy(input, 0, iv, 0, 16);
        System.arraycopy(input, 16, data, 0, data.length);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        return cipher.doFinal(data);
    }

    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
}
